package inference.traffic;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.TreeMap;
import java.util.TreeSet;

import cs534.policy.Function2;

import rddl.RDDL.LCONST;
import rddl.RDDL.PVAR_NAME;
import rddl.RDDL.TYPE_NAME;
import rddl.policy.Policy;
import rddl.State;
import util.Pair;

public class TrafficState{ // implements Comparable<TrafficState> {

	private NavigableSet< Pair<Integer, Integer > > occupied_cells;

	private NavigableMap<Pair<Integer,Integer>,Pair<Boolean,Boolean>> light_signals;

	private NavigableMap< Pair< Integer, Integer >, Boolean > turn_dirxn;

	private static NavigableSet< Pair<Integer, Integer> > input_cells;

	private static NavigableSet< Pair< Integer, Integer> > exit_cells;

	private static int _nRows;

	private static int _nCols;

	private static int _nBlockSize;

	private static int _nWingSize;

	public final static int HASH_INIT = 13;
	public final static int  HASH_SHIFT = 3;

	private static int _carSteps;

	private static NavigableMap<Pair<Integer, Integer>, Pair<Boolean, Boolean>> xion_dirxn;

	private static double _arrivalProb;

	private static Random _rand;

	private static double _turnProb;

	private static NavigableMap<Pair<Integer, Integer>, Pair<Integer, Integer>> next_cells;

	private static NavigableMap<Pair<Integer, Integer>, Pair<Integer, Integer>> prev_cells;

	private static Pair<Integer, Integer> bottomLeft, bottomRight, topLeft, topRight;

	private static int MAXX;

	private static int MAXY;

	private static int _modFactor;
	
	public static String instance_name;
	
	public TrafficState copy()
	{
		return new TrafficState( new TreeSet<Pair<Integer, Integer>>( occupied_cells ), 
								 new TreeMap<Pair<Integer, Integer>, Pair<Boolean, Boolean>>( light_signals ), 
								 new TreeMap<Pair<Integer, Integer>, Boolean>( turn_dirxn ) );
	}
	
	public TrafficState( final NavigableSet<Pair<Integer, Integer>> occupied_cells,
						 final NavigableMap<Pair<Integer,Integer>, Pair<Boolean,Boolean>> light_signals,
						 final NavigableMap<Pair<Integer, Integer>, Boolean> turn_dirxn )
	{
		this.occupied_cells = occupied_cells;
		this.light_signals = light_signals;
		this.turn_dirxn = turn_dirxn;
	}

	public Set<Pair<Integer, Integer>> getOccupiedCells(){
		
		return occupied_cells;
		
	}
	
	public TrafficState( int nrows, int ncols, int blockSize, int wingSize, long rand_seed, 
			double turnProb, double arrivalProb, int horizon, double discount, final int carSteps ){

		_carSteps = carSteps;

		_turnProb = turnProb;

		_arrivalProb = arrivalProb;

		ProblemGenerator prob = new ProblemGenerator(nrows, ncols, wingSize, blockSize,
				turnProb, arrivalProb, horizon, discount, rand_seed);
		
		instance_name = prob.get_instance_name();

		occupied_cells = prob.get_occupied_cells();

		input_cells = prob.get_input_cells();

		exit_cells = prob.get_exit_cells();

		turn_dirxn = prob.get_turn_dirxn();

		next_cells = prob.get_nextCell();

		prev_cells = prob.get_prevCell();

		light_signals = prob.get_light_signals();

		xion_dirxn = prob.get_xion_dirxn();

		_rand = new Random( rand_seed );

		_nRows = nrows;

		_nCols = ncols;

		_nBlockSize = blockSize;

		_nWingSize = wingSize;
		
		assert( _nBlockSize == _nWingSize );

		int maxx_grid = wingSize + (ncols-1) + (ncols-1)*blockSize;
		int maxy_grid = wingSize + (nrows-1) + (nrows-1)*blockSize ;

		bottomLeft = new Pair<Integer, Integer>(wingSize, wingSize);
		bottomRight = new Pair<Integer, Integer>( maxx_grid, wingSize);

		topLeft = new Pair<Integer, Integer>( wingSize, maxy_grid );
		topRight = new Pair<Integer, Integer>( maxx_grid, maxy_grid );

		MAXX = maxx_grid + wingSize;
		MAXY = maxy_grid + wingSize;

		_modFactor = (_nBlockSize+1);

	}

	public void nextState( final NavigableMap< Pair<Integer, Integer>, Pair<Boolean, Boolean> > action ){

		NavigableMap<Pair<Integer, Integer>, Pair<Boolean, Boolean>> act = null;

		for( int k = 0 ; k < _carSteps; ++k ){

			NavigableSet< Pair<Integer, Integer> > newOccupied = new TreeSet< Pair<Integer, Integer> >();

			NavigableMap< Pair<Integer, Integer>, Boolean > next_dxn = new TreeMap<Pair<Integer,Integer>, Boolean>();

			for( Pair<Integer, Integer> cell : exit_cells ){

				Pair<Integer, Integer> curCell = cell;

				Pair<Integer, Integer> prevCell = null;

				while( curCell != null ){

					boolean curOccupied = occupied_cells.contains(curCell);

					Pair<Boolean, Boolean> lightXionCur = light_signals.get( curCell );

					Pair<Boolean, Boolean> xionCur = xion_dirxn.get( curCell );

					if( xionCur == null ){
						prevCell = prev_cells.get( curCell );
					}else{
						prevCell = getPrevCellAtXion( curCell, cell, xionCur );
					}

					boolean prevOccupied = false;
					Pair<Boolean, Boolean> lightXionPrev = null;
					Pair<Boolean, Boolean> xionPrev = null;

					if( prevCell != null ) {
						lightXionPrev = light_signals.get(prevCell);
						xionPrev = xion_dirxn.get(prevCell);
						prevOccupied = occupied_cells.contains(prevCell);
					}

					Pair<Integer, Integer> nextCell = null;
					
					if( xionCur == null ){
						nextCell = next_cells.get( curCell );
					}else{
						boolean futureDirxn = turn_dirxn.get( curCell );
						nextCell = getNextCellAtXion( curCell, cell, xionCur, futureDirxn, curOccupied );
					}
					
					boolean nextOccupied = false;
					Pair<Boolean, Boolean> lightXionNext = null;
					Pair<Boolean, Boolean> xionNext = null;
					if( nextCell != null ) {
						nextOccupied = occupied_cells.contains(nextCell);
						lightXionNext = light_signals.get( nextCell );
						xionNext = xion_dirxn.get(nextCell);
					}
					
					if( curOccupied && xionCur != null ){
						next_dxn.put( curCell, getBernoulli( _rand, _turnProb ) );
					}

//					System.out.println("next : " + nextCell );
//					System.out.println( "prev : " + prevCell );
//					System.out.println( "current : " + curCell );
					
					if( curOccupied && exit_cells.contains(curCell) ){
						occupied_cells.remove( curCell );
						//						continue;
					}
//					else if( !curOccupied && xionCur == null && prevOccupied && xionPrev == null && 
//							!newOccupied.contains(curCell) ){
//						newOccupied.add( curCell );
//						occupied_cells.remove( prevCell );
//					}
					else if( curOccupied &&  xionCur == null && xionNext != null && !nextOccupied && 
							!newOccupied.contains(nextCell) ){
						
						if( goingX(curCell, nextCell) && ( lightXionNext._o1 && !lightXionNext._o2 )
									|| 
							  goingY(curCell, nextCell ) && !lightXionNext._o1 && lightXionNext._o2 ){
							occupied_cells.remove( curCell );
							newOccupied.add( nextCell );
						}else{
							newOccupied.add( curCell );
						}
					
					}
//					else if( !curOccupied && xionCur != null && !newOccupied.contains(curCell) ){
//
//						if( prevOccupied && goingX(prevCell, curCell) && lightXionCur._o1 && !lightXionCur._o2 ){
//							newOccupied.add( curCell );
//						}else if( prevOccupied && goingY(prevCell, curCell) && !lightXionCur._o1 && lightXionCur._o2 ){
//							newOccupied.add( curCell );
//						}
//					}
					else if( curOccupied && xionNext == null && !nextOccupied && 
							nextCell != null && !newOccupied.contains(nextCell) ){
						occupied_cells.remove( curCell );
						newOccupied.add( nextCell );
						//						continue;
					}
//					else if( !curOccupied && xionCur == null && prevOccupied && xionPrev != null && !newOccupied.contains(curCell) ){
//
//						Boolean turn_dxn = turn_dirxn.get(prevCell);
//
//						if( goingX(prevCell, curCell) && turn_dxn ){
//							newOccupied.add( curCell );
//						}else if( goingY( prevCell, curCell ) && !turn_dxn ){
//							newOccupied.add( curCell );
//						}
//
//					}
					else if( !curOccupied && input_cells.contains(curCell) && !newOccupied.contains( curCell ) ){
						if( getBernoulli( _rand, _arrivalProb ) ){

//							System.out.println("new incoming car " + curCell );

							newOccupied.add( curCell );
						}
					}else if( curOccupied && nextCell != null && nextOccupied ){
//						occupied_cells.remove( curCell );
						newOccupied.add( curCell );
					}

					nextCell = curCell;
					curCell = prevCell;

				}

			}

			occupied_cells = null;
			occupied_cells = newOccupied;

			turn_dirxn.putAll(next_dxn);

			if( k == 0 ){
				act = action;
			}else{
				act = null;
			}

			if( act != null ){

				for( Map.Entry<Pair<Integer,Integer>,Pair<Boolean,Boolean>> entry : action.entrySet() ){

					Pair<Integer, Integer> coord = entry.getKey();

					Boolean flipHoriz = entry.getValue()._o1;

					Boolean flipVert = entry.getValue()._o2;

					Pair<Boolean, Boolean> current = light_signals.get( coord );

					if( flipHoriz ){
						current._o1 = !current._o1;
					}

					if( flipVert ){
						current._o2 = !current._o2;
					}

				}

			}

		}

	}

	private Pair<Integer, Integer> getNextCellAtXion(
			Pair<Integer, Integer> curCell, Pair<Integer, Integer> exitCell,
			Pair<Boolean, Boolean> xion_dxn, boolean futureDirxn, boolean curOccupied) {

		Pair<Integer, Integer> nextCell = null;
		
		if( exitCell._o1 == curCell._o1 ){

			if( xion_dxn._o2 && ( curOccupied && !futureDirxn || !curOccupied ) ){//NS
				nextCell = new Pair<Integer, Integer>( curCell._o1, curCell._o2-1 );
			}else if( !xion_dxn._o2 && ( curOccupied && !futureDirxn || !curOccupied ) ){
				nextCell = new Pair<Integer, Integer>( curCell._o1, curCell._o2+1 );
			}

		}else if( exitCell._o2 == curCell._o2 ){

			if( xion_dxn._o1 && ( curOccupied && futureDirxn || !curOccupied ) ){//EW
				nextCell = new Pair<Integer, Integer>( curCell._o1-1, curCell._o2 );
			}else if( !xion_dxn._o1 && ( curOccupied && futureDirxn || !curOccupied ) ){//WE
				nextCell = new Pair<Integer, Integer>( curCell._o1+1, curCell._o2 );
			}

		}else{
			try {
				throw new Exception("inconsistent");
			} catch (Exception e) {
				e.printStackTrace();
				System.exit(1);
			}
		}

		return nextCell;
		
	}

	private Pair<Integer, Integer> getPrevCellAtXion(
			Pair<Integer, Integer> curCell, Pair<Integer, Integer> exitCell,
			Pair<Boolean, Boolean> xion_dxn) {

		Pair<Integer, Integer> prevCell = null;
		
		if( exitCell._o1 == curCell._o1 ){

			if( xion_dxn._o2 ){//NS
				prevCell = new Pair<Integer, Integer>( curCell._o1, curCell._o2+1 );
			}else{
				prevCell = new Pair<Integer, Integer>( curCell._o1, curCell._o2-1 );
			}

		}else if( exitCell._o2 == curCell._o2 ){

			if( xion_dxn._o1 ){//EW
				prevCell = new Pair<Integer, Integer>( curCell._o1+1, curCell._o2 );
			}else{
				prevCell = new Pair<Integer, Integer>( curCell._o1-1, curCell._o2 );
			}

		}else{
			try {
				throw new Exception("inconsistent");
			} catch (Exception e) {
				e.printStackTrace();
				System.exit(1);
			}
		}

		return prevCell;
		
	}

	private boolean goingX(Pair<Integer, Integer> curCell,
			Pair<Integer, Integer> nextCell) {

		return( curCell._o2 == nextCell._o2 && Math.abs( curCell._o1 - nextCell._o1 ) == 1 );
		
	}
	
	private boolean goingY(Pair<Integer, Integer> curCell,
			Pair<Integer, Integer> nextCell) {

		return( curCell._o1 == nextCell._o1 && Math.abs( curCell._o2 - nextCell._o2 ) == 1 );
		
	}

	public static boolean getBernoulli(Random rand, double prob) {

		double r = rand.nextDouble();

		return ( r < prob );

	}

	public double getReward( ){

		double ret = 0.0d;

		for( Pair<Integer, Integer> cell : occupied_cells ){

			Pair<Integer, Integer> nextCell = next_cells.get( cell );

			boolean curOccupied = occupied_cells.contains( cell );

			Pair<Boolean, Boolean> xionCur = light_signals.get( cell );

			if( curOccupied && xionCur != null ){

				boolean futureDirxn = turn_dirxn.get( cell );

				Pair<Boolean, Boolean> xion_dxn = xion_dirxn.get( cell );

				if( futureDirxn ){

					if( xion_dxn._o1 ){
						nextCell = new Pair<Integer, Integer>(cell._o1-1, cell._o2);	
					}else{
						nextCell = new Pair<Integer, Integer>(cell._o1+1, cell._o2);
					}

				}else if( !futureDirxn  ){

					if( xion_dxn._o2 ){
						nextCell = new Pair<Integer, Integer>(cell._o1, cell._o2-1);	
					}else{
						nextCell = new Pair<Integer, Integer>(cell._o1, cell._o2+1);
					}

				}

			}

			boolean nextOccupied = false;
			Pair<Boolean, Boolean> xionNext = null;
			if( nextCell != null ) {
				nextOccupied = occupied_cells.contains(nextCell);
				xionNext = xion_dirxn.get( nextCell );
			}
			
			if( curOccupied && nextOccupied ){
				ret -= 1;
			}

			if( curOccupied && xionNext != null && !nextOccupied ){
				
				Pair<Boolean, Boolean> lightNext = light_signals.get(nextCell);
				assert( lightNext != null );
				
				if( cell._o1 == nextCell._o1 ){
					
					if( xionNext._o2 && cell._o2 - nextCell._o2 == 1 && 
							!( !lightNext._o1 && lightNext._o2 ) ){
						//NS - cant move
						ret -= 1;
					}else if( !xionNext._o2 && cell._o2 - nextCell._o2 == -1 &&
							!( !lightNext._o1 && lightNext._o2 ) ){
						ret -= 1;//SN cant move
					}
					
				}else if( cell._o2 == nextCell._o2 ){
					
					if( xionNext._o1 && cell._o1 - nextCell._o1 == 1 && 
							!( lightNext._o1 && !lightNext._o2 ) ){
						//EW - cant move
						ret -= 1;
					}else if( !xionNext._o1 && cell._o1 - nextCell._o1 == -1 &&
							!( lightNext._o1 && !lightNext._o2 ) ){
						ret -= 1;//SN cant move
					}
					
				}
				
			}
			
		}
		
//		for( Pair<Boolean, Boolean> light : light_signals.values() ){
//			if( light._o1 && light._o2 ){
//				ret -= 10;
//			}
//		}

		return ret;

	}

	public static class LocalState
	{
		final int light_signal;//0 - red, 1 - green vert, 2 - green horiz, 3 - all green
		final boolean[] occupancy;//ascending x, then y - same order of keySet of treemap
		final boolean turn;
		//DO NOT CHANGE THE REPRESENTATION - EVER!
		public LocalState( final int light_signal, final boolean[] occupancy, final boolean turn )
		{
			this.light_signal = light_signal;
			this.occupancy = occupancy;
			this.turn = turn;
		}

		public LocalState( LocalState ls ){
			this( ls.light_signal, ls.occupancy, ls.turn );
		}
		
		public void printHeader( final PrintStream out ){
			
			out.print("action, ");
			
			for( int d = 0; d < 4; ++d ) {
				for( int a = 0; a < 4; ++a ) {
					if( d != 0 || a != 0 ) {
						out.print( "," );
					}
					out.print( "d" + d + "_s" + a );
				}
			}
			out.println();
		}
		
		public void printTo( final PrintStream out, final CRFStructure crf_structure, 
				final Pair<Integer, Integer> position, 
							 int action ) {
			
			out.print( action  );
			out.print(", ");
			
			boolean comma = false;
			for( int d = 0; d < 4; ++d ) {
				for( int sprime = 0; sprime < 4; ++sprime ) {
					if( comma ) {
						out.print( "," );
					}
					comma = true;
					out.print( CRFStructure.fullnessXnext_signal( 
						this, action, crf_structure.makeNeighborIndices( d ), sprime ) );
				}
			}
			out.println();
		}
		
//		public void printHeader( String fileName ){
//			BufferedWriter bwr;
//			try {
//				bwr = new BufferedWriter( new FileWriter( fileName, false ) );
//				bwr.write("light_signal, turn, flip, ");
//				for( int i = 0 ; i < occupancy.length; ++i ){
//					bwr.write("c");
//					bwr.write(i+"");
//					bwr.write( ""+ (( i == occupancy.length-1 ) ? "\n" : ",") );
//				}
//				bwr.close();
//			} catch (IOException e) {
//				e.printStackTrace();
//			}
//			
//		}
//		
//		public void printTo(String fileName, boolean append, boolean flip) {
//			BufferedWriter bwr;
//			try {
//				bwr = new BufferedWriter( new FileWriter( fileName, append ) );
//				bwr.write( "" + light_signal );
//				bwr.write(", ");
//				bwr.write( "" + ( turn ? 1 : 0 ) );
//				bwr.write(", ");
//				bwr.write("" + ( flip ? 1 : 0 ) );
//				bwr.write(", ");
//				
//				for( int i = 0 ; i < occupancy.length; ++i ){
//					bwr.write( "" + ( occupancy[i] ? 1 : 0 ) );
//					bwr.write( "" + ( ( i == occupancy.length - 1 ) ? "\n" : ", " ));
//				}
//				bwr.close();
//			} catch (IOException e) {
//				e.printStackTrace();
//			}
//			
//		}

		public double multiply(ArrayList<Double> params, final CRFStructure crf_structure,
				int action) {
			
			double ret = params.get(0);
			
			for( int d = 0; d < 4; ++d ) {
				for( int sprime = 0; sprime < 4; ++sprime ) {

					double thisFeature = CRFStructure.fullnessXnext_signal( 
						this, action, crf_structure.makeNeighborIndices( d ), sprime );
					
					double param = params.get( 4*d + sprime );
					
					ret += thisFeature * param;
					
				}
			}
			
//			ret += params.get(1)*light_signal;
//			ret += params.get(2)*(turn?1:0);
//			
//			for( int i = 0 ; i < occupancy.length; ++i ){
//				ret += params.get(i+2)*(occupancy[i] ? 1 : 0);
//			}
//			
//			ret = 1 + Math.exp(-ret);
//			ret = 1 / ret;
			
//			System.err.println( ret );
			
			return ret;
			
		}
		
	}
	
	/**
	 * Intersection -> Horizontal light: {c<x>_<y>, flip, light-signal, turn} -> Boolean
	 * 				-> Vertical light: {c<x>_<y>, flip, light-signal, turn} -> Boolean
	 * @return
	 */
	public NavigableMap<Pair<Integer, Integer>, LocalState>	getLocalStateDescriptions()
	{	
		final NavigableMap< Pair<Integer, Integer>, LocalState > ret = new TreeMap<Pair<Integer,Integer>, LocalState >();
		
		for( Map.Entry< Pair<Integer, Integer>, Pair< Boolean, Boolean > > entry
				: light_signals.entrySet() ){
			
			Pair<Integer, Integer> xion = entry.getKey();
			ArrayList< Pair<Integer, Integer> > nbrs = getLocalTiles( xion );
			
//			TreeMap<String, Boolean> inner = new TreeMap< String, Boolean>();
//			inner.put( "turn", turn_dirxn.get(xion) );
//			TreeMap<String, Boolean> copy = new TreeMap< String, Boolean >( inner );
//			inner.put( "light-signal", entry.getValue()._o1 );
//			copy.put( "light-signal", entry.getValue()._o2 );
			
			final boolean[] occupancy = getOccupancy( nbrs );
			final boolean turn = turn_dirxn.get( xion );
			final int signal = ((entry.getValue()._o1.booleanValue() ? 1 : 0) << 1)
							 | (entry.getValue()._o2.booleanValue() ? 1 : 0);
			
			ret.put( xion, new LocalState( signal, occupancy, turn ) );
			
		}
		
		return ret;
		
	}

	
	

	private boolean[] getOccupancy( final ArrayList<Pair<Integer, Integer>> nbrs )
	{
		final boolean[] result = new boolean[nbrs.size()];
//		if( occupied_cells.isEmpty() ) {
//			return result;
//		}
		
		int idx = 0;
//		System.out.println( "vvvvv" );
//		System.out.println( nbrs.size() );
		for( final Pair<Integer, Integer> p : occupied_cells ) {
			
//			System.out.println( "Cell = " + p );
			
			while( idx < nbrs.size() && p.compareTo( nbrs.get( idx ) ) > 0 ) {
				++idx;
			}
			if( idx >= nbrs.size() ) {
				break;
			}
//			System.out.println( "idx = " + idx );
			final int cmp = p.compareTo( nbrs.get( idx ) );
//			System.out.println( "Neighbor = " + nbrs.get( idx ) );
			if( cmp == 0 ) {
				result[idx] = true;
			}
		}
//		System.out.println( "^^^^^" );
		
		return result;
	}
	
	private NavigableMap<Pair<Integer, Integer>, ArrayList<Pair<Integer, Integer>>> 
		local_tile_cache = new TreeMap<Pair<Integer, Integer>, ArrayList<Pair<Integer, Integer>>>();

	/**
	 * Given an intersection, returns all neighboring tiles in its local state,
	 * including the intersection, sorted by (x, y).
	 * @param coord
	 * @return
	 */
	private ArrayList<Pair<Integer, Integer>> getLocalTiles(
			Pair<Integer, Integer> coord )
	{
		ArrayList< Pair<Integer, Integer> > ret = local_tile_cache.get( coord );
		
		if( ret == null ) {
			ret = new ArrayList< Pair< Integer, Integer > >( 4 * _nBlockSize + 1 );
		
			final int x = coord._o1;
			final int y = coord._o2;
			
			// Sort by x, then y
			for( int dx = -_nBlockSize; dx <= _nBlockSize; ++dx ) {
				if( dx == 0 ) {
					for( int dy = -_nBlockSize; dy <= _nBlockSize; ++dy ) {
						ret.add( new Pair<Integer, Integer>( x, y + dy ) );
					}
				}
				else {
					ret.add( new Pair<Integer, Integer>( x + dx, y ) );
				}
			}
			
			local_tile_cache.put( coord, ret );
		}
		
		return ret;
	}

	public static void main(String[] args) throws Exception {
//
////		TrafficState ts = new TrafficState(4, 4, 2, 2, 42, 0.3, 0.1, 20, 0.9, 2 );
		TrafficState ts = new TrafficState( 2, 2, 4, 4, 42, 0.3, 0.1, 100, 0.9, 1 );
//
//		NavigableMap< Pair<Integer, Integer>, Pair<Boolean,Boolean> > flipHorizAction 
//		= new TreeMap<Pair<Integer,Integer>, Pair<Boolean,Boolean>>();
//
//		NavigableMap< Pair<Integer, Integer>, Pair<Boolean,Boolean> > flipVertAction 
//		= new TreeMap<Pair<Integer,Integer>, Pair<Boolean,Boolean>>();
//		
//		NavigableSet<Pair<Integer, Integer>> xions = ts.get_xions();
//		
//		for( Pair<Integer, Integer> xion : xions ){
//			flipHorizAction.put( xion, new Pair<Boolean,Boolean>(true, false) );
//			flipVertAction.put( xion, new Pair<Boolean,Boolean>(false, true) );
//		}
//		
		TrafficDisplay viz =  new TrafficDisplay(1000);
//		
//		NavigableMap<Pair<Integer, Integer>, Pair<ArrayList<Double>, ArrayList<Double>>>
//			logisticParams = null;
//		
//		while( true ){
//			
////			System.out.println( logisticParams );
//			
//			TreeMap<Pair<Integer, Integer>, 
//				ArrayList< Pair< LocalState, Pair<Boolean, Boolean > > > > logisticInstances 
//						= new TreeMap< Pair<Integer, Integer>, 
//							ArrayList< Pair< LocalState, Pair<Boolean, Boolean > > > >();
//
			int i = 0;
			
			while(  i++ < 100  ){
	
				System.out.println( ts.toString() );
	
				if( viz != null ){
					viz.display(ts);
				}
				
				System.out.println( ts.getReward() );
//
//
//				NavigableMap<Pair<Integer, Integer>, LocalState> thisInstance 
//					= ts.getLocalStateDescriptions( );
//				
//				NavigableMap<Pair<Integer, Integer>, Pair<Boolean, Boolean>> action 
//					= LogisticRegression.sampleLocalActions( LogisticRegression.getLocalActions( thisInstance,  logisticParams ) );
//						
				ts.nextState( null );
			}
//				
//				
//				ts.addToInstances( logisticInstances, addAction( thisInstance, action ) ); 
//				
//	//						try {
//	//							System.out.println("key press");
//	//							System.in.read();
//	//						} catch (IOException e) {
//	//							e.printStackTrace();
//	//						}
//	
//			}
//	
//			logisticParams = LogisticRegression.learnLogisticRegression( logisticInstances );
//			
//			System.out.println( logisticParams );
//		
//		}
		
	}

	

	private static NavigableMap<Pair<Integer, Integer>, Pair<LocalState, Pair<Boolean, Boolean> >> addAction(
			NavigableMap<Pair<Integer, Integer>, LocalState> thisInstance, 
			NavigableMap<Pair<Integer, Integer>, Pair<Boolean, Boolean>> action) {
		
		 NavigableMap<Pair<Integer, Integer>, Pair<LocalState, Pair<Boolean, Boolean > >> ret 
		 	= new TreeMap<Pair<Integer, Integer>, Pair<LocalState, Pair<Boolean, Boolean> > > ();
		
		 for( Map.Entry<Pair<Integer, Integer>, Pair<Boolean, Boolean>> entry : action.entrySet() ){
			 ret.put( entry.getKey(), new Pair<LocalState, Pair<Boolean, Boolean>>(
					 			thisInstance.get( entry.getKey() ), 
					 			( entry.getValue() ) ) );
		 }
		 
		 return ret;
		 
	}

	private void addToInstances(
			NavigableMap<Pair<Integer,Integer>,ArrayList<Pair<LocalState,Pair<Boolean,Boolean>>>> logisticInstances,
			NavigableMap<Pair<Integer,Integer>,Pair<LocalState,Pair<Boolean,Boolean>>> thisInstance) {
		
		for( Entry<Pair<Integer, Integer>, Pair<LocalState, Pair<Boolean, Boolean>>> thisEntry 
					: thisInstance.entrySet() ){
			
			ArrayList<Pair<LocalState, Pair<Boolean, Boolean>>> thoseInstances 
				= logisticInstances.get( thisEntry.getKey() );
			
			if( thoseInstances == null ){
				
				ArrayList<Pair<LocalState, Pair<Boolean, Boolean>>> newList 
					= new ArrayList< Pair<LocalState,  Pair<Boolean, Boolean> > >( );
				
				logisticInstances.put( thisEntry.getKey(), newList );
			}

			ArrayList<Pair<LocalState,  Pair<Boolean, Boolean> > > list 
				= logisticInstances.get( thisEntry.getKey() );	
			list.add( new  Pair<LocalState,  Pair<Boolean, Boolean> >( 
					thisEntry.getValue()._o1, thisEntry.getValue()._o2 ) );
						
		}
		
		return;
		
	}

	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();

		sb.append("occupied : " );
		sb.append( occupied_cells.toString() );
		sb.append("\n");

		sb.append("lights  : " );
		sb.append( light_signals.toString() );
		sb.append("\n");

		sb.append("turns : ");
		sb.append(turn_dirxn.toString());
		sb.append("\n");

		return sb.toString();

	}

	public NavigableMap<Pair<Integer, Integer>, Pair<Boolean, Boolean>> get_lights(){
		return light_signals;
	}

	public NavigableSet<Pair<Integer, Integer>> get_xions( ){
		return xion_dirxn.navigableKeySet();
	}
	
	public int getNumIntersections()
	{
		return xion_dirxn.size();
	}

	public Pair<Boolean, Boolean> get_xion_dirxn(int i, int j) {
		return xion_dirxn.get( new Pair<Integer, Integer>(i,j) );
	}

	public int get_nRows() {
		return _nRows;
	}

	public int get_nCols() {
		return _nCols;
	}

	public int get_nBlockSize() {
		return _nBlockSize;
	}

	public int get_nWingSize() {
		return _nWingSize;
	}


}
//	@Override
//	public int hashCode() {
//	
//		//hashcode is function of occupied cells 
//		//and dirxn of those cells
//		//and the state of the lights
//		
//		if( hashSet ){
//			return _nHash;
//		}
//		
//		int hash = HASH_INIT;
//
//		
//		
//		for( LCONST cell : cells ){
//		
//			tempCell.set(0, cell);
//			
//			boolean occupado = (Boolean)(_state.getPVariableAssign(occupied, tempCell));
//			
//			if( occupado ){
//				
//				String cellName = cell._sConstValue;
//				
//				cellName = cellName.substring(1);
//				
//				String[] dump = cellName.split("_");
//				
//				assert( dump.length == 2 );
//				
//				int x = Integer.parseInt(dump[0]);
//				
//				int y = Integer.parseInt( dump[1] );
//				
//				hash = ( hash << HASH_SHIFT - hash ) + x;
//				
//				hash = ( hash << HASH_SHIFT - hash ) + y;
//				
//				hash = ( hash << HASH_SHIFT - hash ) + x*x;
//				
//				hash = ( hash << HASH_SHIFT - hash ) + y*y;
//				
//			}
//		
//		}
//		
//		for( LCONST xion : intersections ){
//			
//			tempXion.set(0, xion);
//			
//			boolean dirxn = (Boolean)_state.getPVariableAssign(direction, tempXion);
//					
//			boolean turn = (Boolean) _state.getPVariableAssign(turn_at, tempXion);
//			
//			boolean light1 = (Boolean) _state.getPVariableAssign(light_signal1, tempXion);
//			
//			boolean light2 = (Boolean) _state.getPVariableAssign(light_signal2, tempXion);
//		
//			if( light1 && !light2 || !light1 && light2 ){
//				//that is, not red
//				String xionName = xion._sConstValue;
//				
//				xionName = xionName.substring(1);
//				
//				String[] dump = xionName.split("_");
//				
//				assert( dump.length == 2 );
//				
//				int x = Integer.parseInt(dump[0]);
//				
//				int y = Integer.parseInt( dump[1] );
//				
//				hash = ( hash << HASH_SHIFT - hash ) + x;
//				
//				hash = ( hash << HASH_SHIFT - hash ) + y;
//				
//				hash = ( hash << HASH_SHIFT - hash ) + x*x;
//				
//				hash = ( hash << HASH_SHIFT - hash ) + y*y;
//		
//				tempBS_lol.set(0, light1);
//				
//				tempBS_lol.set(1, light2);
//				
//				tempBS_lol.set(2, dirxn);
//				
//				tempBS_lol.set(3, turn);
//
//				hash = ( hash << HASH_SHIFT - hash ) + tempBS_lol.hashCode();
//				
//				tempBS_lol.clear();
//
//			}
//			
//		}
//		
//		_nHash = hash;
//		hashSet = true;
//	
//		return hash;
//		
//	}

//	public State getState() {
//		return _state;
//	}
//	
//	public void setState(State rddlState) {
//		this._state = _state;
//	}

//	@Override
//	public int compareTo(TrafficState o) {
//
//		State other = o.getState();
//		
//		for( LCONST cell : cells ){
//			
//			tempCell.set(0, cell);
//			
//			boolean occupadoThis = (Boolean)(_state.getPVariableAssign(occupied, tempCell));
//		
//			boolean occupadeOther = (Boolean)(other.getPVariableAssign(occupied, tempCell));
//			
//			if( occupadoThis == occupadeOther ){
//
//				continue;
//				
//			}else{
//				return occupadoThis ? 1 : -1;
//			}
//			
//		}
//		
//		for( LCONST xion : intersections ){
//			
//			tempXion.set(0, xion);
//			
//			boolean dirxn = (Boolean)_state.getPVariableAssign(direction, tempXion);
//					
//			boolean turn = (Boolean) _state.getPVariableAssign(turn_at, tempXion);
//			
//			boolean light1 = (Boolean) _state.getPVariableAssign(light_signal1, tempXion);
//			
//			boolean light2 = (Boolean) _state.getPVariableAssign(light_signal2, tempXion);
//		
//			boolean dirxnOther = (Boolean)other.getPVariableAssign(direction, tempXion);
//			
//			boolean turnOther = (Boolean) other.getPVariableAssign(turn_at, tempXion);
//			
//			boolean light1Other = (Boolean) other.getPVariableAssign(light_signal1, tempXion);
//			
//			boolean light2Other = (Boolean) other.getPVariableAssign(light_signal2, tempXion);
//			
//			if( ( light1 ^ light1Other ) && ( light2 ^ light2Other ) && ( dirxn ^ dirxnOther ) 
//					&& ( turn ^ turnOther ) ){
//				continue;
//			}else{
//				
//				if( light1 || light2 || dirxn || turn ){
//					return 1;
//				}else{
//					return -1; 
//				}
//				
//			}
//			
//			
//		}
//		
//		return 0;
//			
//	}
